import loading from '@/utils/loading'
import { useRoute } from 'vue-router'
import { onUnmounted, reactive, Ref } from 'vue'
import { SelectUploadFile } from './useUpload'
import mime from 'mime'
import { IS_ELECTRON } from '@/store/useEnv'
import _ from 'lodash'
import { DesktopCapturerSource, IpcRenderer } from 'electron'
import { useNotification, useMessage } from '@/main'
import { $postMessage } from '@/utils/ipcRenderer'
import { useI18n } from 'vue-i18n'

let ipcRenderer: IpcRenderer
if (IS_ELECTRON) {
  ipcRenderer = require('electron').ipcRenderer
}

/** 选择的视频流数据源，仅 electron 使用 */
export type SourceType = 'screen'
type Source = DesktopCapturerSource & { img: string }

/** 浏览器支持的视频 mime 类型 */
const SUPPORT_VIDEO_MIME_TYPE = [
  'video/webm;codecs=vp8',
  'video/webm;codecs=daala',
  'video/webm;codecs=h264',
  'video/webm',
  'video/mpeg'
].find(item => MediaRecorder.isTypeSupported(item)) as string

type Props = {
  /** 将要上传的文件列表 */
  selectUploadFileList: Ref<SelectUploadFile[]>
}
export default ({ selectUploadFileList }: Props) => {
  const route = useRoute()
  const notification = useNotification()
  const { t } = useI18n()
  const state = reactive({
    /** 录制的视频路径 */
    url: '',
    /** 录制的视频预览是否显示 */
    show: false,
    /** 录制的视频 File 对象，用于上传 */
    file: null as SelectUploadFile | null,
    /** 媒体录制器 */
    mediaRecorder: null as MediaRecorder | null,
    /** 选中的视频流数据配置项，仅在 electron 客户端使用，web 端浏览器自带选择视频源的功能 */
    desktopSource: {
      /** 选择视频流的弹窗是否显示 */
      show: false,
      /** 当前选择的视频流的选项卡类型 */
      activeTab: 'screen' as SourceType,
      /** 开始录制倒计时秒 */
      countdown: 3,
      /** 开始录制计时器 */
      timer: undefined as any,
      /** 可选择的视频数据源类型列表 */
      sources: {
        /** 屏幕视频源列表，用户可能有多个屏幕 */
        screen: [] as Source[],
        /** 浏览器标签视频源列表 */
        window: [] as Source[]
      }
    }
  })

  /** 开始录制视频，同时兼容 web端 和 客户端 */
  let startRecordVideo: () => Promise<void>
  if (IS_ELECTRON) {
    // 兼容客户端
    startRecordVideo = async () => {
      loading.add('加载视频列表中')
      const { desktopCapturer } = await import('electron')
      const sources = _(await desktopCapturer.getSources({ types: ['screen' /**, 'window' */] as SourceType[] }))
        .map(item => ({ ...item, img: item.thumbnail.toDataURL() }))
        .groupBy(item => item.id.match(/[^:]+/)![0] as SourceType)
        .value() as typeof state.desktopSource.sources

      state.desktopSource.show = true
      state.desktopSource.sources = sources
      state.desktopSource.activeTab = 'screen'
      loading.remove()
    }
  } else {
    // 兼容 web 端
    startRecordVideo = async () => {
      try {
        const stream = await navigator.mediaDevices.getDisplayMedia({
          audio: true,
          video: {
            width: 1920,
            height: 1080
          }
        })
        state.mediaRecorder = new MediaRecorder(stream)
        recorderVideo()
      } catch (err: any) {
        if (err?.message === 'Permission denied') {
          useMessage().info(t('cancel-record-video'))
          return
        }
        // 如果拒绝授权或取消录制视频或是其他错误
        useMessage().error(err?.message || t('get-video-source-error'))
      }
    }
  }

  const recorderVideo = () => {
    state.mediaRecorder!.start()
    state.mediaRecorder!.onerror = () =>
      notification.error({
        title: '媒体录制器播放异常'
      })
    state.mediaRecorder!.ondataavailable = e => {
      state.desktopSource.countdown = 3
      if (IS_ELECTRON) {
        $postMessage(({ globalShortcut }) => globalShortcut.unregister('Esc'))
        ipcRenderer.removeListener('stop-recorder-video', listenKyeupByStopRecorder)
      }

      const type = SUPPORT_VIDEO_MIME_TYPE
      const blob = new Blob([e.data], { type })
      state.url = URL.createObjectURL(blob)
      state.show = true
      const fileName = Date.now() + '.' + (mime as any).extension(type) || 'mp4'
      const file = new File([blob], fileName, { type }) as SelectUploadFile
      Object.defineProperties(file, {
        path: { value: '' },
        parentPath: { value: route.query.parentPath || '' }
      })
      state.file = file
    }
  }
  /** 监听键盘按下 Esc 结束录制视频 */
  const listenKyeupByStopRecorder = () => {
    if (state.desktopSource.countdown > 0) {
      clearInterval(state.desktopSource.timer)
      state.desktopSource.countdown = 3
      useMessage().success('已取消录制视频')
      $postMessage(({ globalShortcut }) => globalShortcut.unregister('Esc'))
    } else {
      state.mediaRecorder!.stop()
      $postMessage(({ mainWin }) => mainWin.show())
    }
  }

  /** 选择视频源（pc端） */
  const selectSource = async (source: Source) => {
    state.desktopSource.show = false
    await new Promise(resolve => {
      $postMessage(({ globalShortcut, mainWin }) => {
        globalShortcut.register('Esc', () => mainWin.webContents.send('stop-recorder-video'))
      })
      ipcRenderer.once('stop-recorder-video', listenKyeupByStopRecorder)

      useMessage().success('按 Esc 键结束录制视频')
      state.desktopSource.timer = setInterval(() => {
        if (!state.desktopSource.countdown) {
          $postMessage(e => e.mainWin.minimize())
          clearInterval(state.desktopSource.timer)
          return resolve(1)
        } else {
          state.desktopSource.countdown--
          useMessage().success(`${state.desktopSource.countdown + 1}秒后开始录像`)
        }
      }, 1000)
    })

    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSourceId: source.id,
            chromeMediaSource: 'desktop',
            minWidth: 1920,
            minHeight: 1080
          }
        }
      } as MediaStreamConstraints)
      state.mediaRecorder = new MediaRecorder(stream, { mimeType: SUPPORT_VIDEO_MIME_TYPE })
      recorderVideo()
    } catch (err: any) {
      notification.error({
        title: '获取视频流失败',
        description: err.message
      })
      Promise.reject(err.message)
    }
  }

  onUnmounted(async () => {
    if (!IS_ELECTRON) return
    $postMessage(({ globalShortcut }) => globalShortcut.unregister('Esc'))
    ipcRenderer.removeListener('stop-recorder-video', listenKyeupByStopRecorder)
  })

  /** 上传视频 */
  const uploadVideo = () => {
    state.show = false
    state.url = ''
    selectUploadFileList.value = [state.file as SelectUploadFile, ...selectUploadFileList.value]
  }

  return {
    state,
    startRecordVideo,
    selectSource,
    uploadVideo
  }
}
